# 示例运行多种并发方式


import asyncio

from utils import timer


async def greets(name, delay):
    await asyncio.sleep(delay)  # 模拟耗时操作，这里使用 asyncio.sleep 来模拟异步等待
    return f"See U after {delay}s, {name}!"  # 返回一个字符串


async def greeter(name, delay):
    await asyncio.sleep(delay)
    if name == "Bob":
        raise ValueError("Bob is not allowed")
    return f"See U after {delay}s, {name}!"


async def greet(name, delay):
    await asyncio.sleep(delay)
    return f"hello {name}"


@timer  # 使用 timer 装饰器来计时
async def sync_main():  # 定义一个异步函数 main；但是执行方式是同步的
    #
    print("Running in synchronous mode...")
    sync_rst1 = await greets("Alice", 1)
    sync_rst2 = await greets("Amy", 2)
    print(sync_rst1)  # 打印同步执行的结果
    print(sync_rst2)  # 打印同步执行的结果


## 对比/异步执行 ##
@timer
async def main():
    # 通过创建任务的方式，多个任务可以同时运行，事件循环会在任务之间切换，异步执行 greets 函数
    task1 = asyncio.create_task(greets("Alice", 1))
    task2 = asyncio.create_task(greets("Bob", 2))

    rst1 = await task1  # 等待 task1 完成并获取结果
    print(rst1)  # 打印 task1 的结果
    rst2 = await task2  # 等待 task2 完成并获取结果
    print(rst2)  # 打印 task2 的结果


## 第二种异步执行，Taskgroup方式,在任务组中创建任务##


@timer
async def main_taskgroup():
    try:
        async with asyncio.TaskGroup() as tg:
            task1 = tg.create_task(greets("alice in main_taskgroup", 1))
            task3 = tg.create_task(greeter("Bob", 3))
            task2 = tg.create_task(greets("amy in main_taskgroup", 2))
    except ExceptionGroup as eg:  # 此处将打印所有的traceback  # noqa: F821
        for e in eg.exceptions:
            print(f"Error: {e}")
        # return  # 停止退出，完全消化异常

    ## 2. 存在任务失败的情况，立即取消所有任务，抛出 ExceptionGroup##
    # 使用greeter函数模拟
    print(task1.done())
    print(task3.done())
    print(task2.done())


## 第三种 gather方法并发
@timer
async def main_gather():
    # gather 将所有协程全部执行成功之后，会按照协程入队的顺序，将协程的返回值存放在results中
    try:
        results = await asyncio.gather(
            greet("amy", 1),
            greeter("Bob", 3),  # greeter 模拟一个错误
            greet("cline", 2),
            return_exceptions=True,
        )
    except Exception as e:
        print(e)
    tasks = asyncio.all_tasks()
    for task in tasks:
        # 此处依次检查协程任务，并确认有没有被取消
        print(task.get_name(), task.done())
        # if判断是为来解决协程中await自身的情况，因为await自身，会返回None，所以需要排除掉
        # Task-1 代表的是 gather 中的第1个协程
        if task.done():
            try:
                result = await task
                print(f"Result: {result}")
            except Exception as e:
                print(f"Error: {e}")
    print(results)


@timer
async def main_as_completed():
    # 使用 asyncio.as_completed 来获取协程的结果
    iterable_list = [greet("amy", 1), greet("cline", 3)]
    try:
        for corot in asyncio.as_completed(iterable_list, timeout=2):
            # 此处增加超时时间，如果没有能够成功执行，报错超时。2 秒后不等了，但任务还在跑
            # 如果不设置超时时间，会一直等下去，直到所有任务都完成

            result = await corot
            print(result)
    except Exception as e:
        print(f"Error timeout {e}")
    tasks = asyncio.all_tasks()  # 获取程序创建的所有任务；主任务+子任务
    # 后续代码抓到未完成的 greet("cline", 3) ，等它到 3 秒完成 → 打印 hello cline
    for task in tasks:
        # print(task.get_name(), task.cancelled())
        if task.get_name() != "Task-1":  # 排除主任务
            result = await task
            print(f"Result: {result}")


if __name__ == "__main__":  # 检查当前模块是否作为主程序运行
    # asyncio.run(main())  # 运行 main 函数，启动事件循环并执行异步任务
    # asyncio.run(sync_main())
    # asyncio.run(main_taskgroup())
    # asyncio.run(main_gather())
    asyncio.run(main_as_completed())
